iT邦幫忙

2023 iThome 鐵人賽

DAY 8
0
Modern Web

Phoenix 1.7 完全教學系列 第 8

8 Schema

  • 分享至 

  • xImage
  •  

在做畫面之前,通常都先把這次的商業邏輯處理好放在 Context 裡面,到時候實作畫面互動的時候直接互叫我們做好的 Context 函式即可,這個章節的目標是完成功能所需要的函式,並可以在 iex 裡面操作整個流程。

在這次的示範專案 Gratitude,我們要做的是感激筆記,其實結構上比較像是短的部落格或是留言板,需要儲存的格式相對簡單。

Schema 產生器

首先我們先決定資料的樣子,目前我們唯一需要的欄位只有內容而已,另外 Phoenix 會幫我們加上 id、inserted_at、updated_at 這三個欄位。

Elixir 用來操作資料庫的 Ecto 內建在 Phoenix 裡面,這個套件提供了我們產生 Schema 的指令,我們先執行空的看看他會給什麼建議

mix phx.gen.schema
** (Mix) Invalid arguments

mix phx.gen.schema expects both a module name and
the plural of the generated resource followed by
any number of attributes:

    mix phx.gen.schema Blog.Post blog_posts title:string

mix phx.gen.schema 後面加上我們要的 Schema module 名稱,資料庫的表格名,以及我們要的欄位。

雖然 Schema module 的名稱可以隨便取,但為了之後方便好找,絕大多數的時候我們都使用 Context名.Schema名 這種格式,所以我們這次 Context 叫 Notes 的 Schema 叫 Note。指令就變成:

mix phx.gen.schema Notes.Note notes content:string

執行後:

* creating lib/gratitude/notes/note.ex
* creating priv/repo/migrations/20230922133805_create_notes.exs

Remember to update your repository by running migrations:

    $ mix ecto.migrate

我們多了兩個檔案,第一個是我們的 Schema module lib/gratitude/notes/note.ex
第二個是用來建立資料庫表格的 migration 檔案 priv/repo/migrations/20230922133805_create_notes.exs

Schema

我們先看看 Schema module,他主要分為兩個區塊:

schema macro

schema "notes" do
  field :content, :string

  timestamps()
end

這個區塊用來定義資料庫取出的資料對應到 Elixir 的型別,這邊我們只有一個欄位,所以只有一行 field :content, :string,如果有多個欄位,就會有多行 field
另一個 timestamps() 函式則為我們加上 inserted_atupdated_at 這兩個欄位。

changeset 函式

def changeset(note, attrs) do
  note
  |> cast(attrs, [:content])
  |> validate_required([:content])
end

這個是當我們要儲存資料之前會使用的函式,首先會執行 cast/2 先把被輸入的對象與輸入的資料轉成 Changeset(改變集),再使用 validate_ 開頭的檢查函式來確認資料是否符合我們的規範,目前預設的給的是檢查 :content 是否存在。

Migration

接下來我們來看看 migration 檔案 priv/repo/migrations/20230922133805_create_notes.exs

def change do
  create table(:notes) do
    add :content, :string

    timestamps()
  end
end

可以看到這邊的 change 函式,在我們執行 資料庫 migration 的時候如果這個 migration 檔案還沒有執行過的話就會被執行, 裡面的則是這次要執行的內容。

這邊產生的函式都算是直覺,建立一個名為 notes 的資料表,裡面有一個 content 欄位,還有跟剛剛在 schema 中的類似會幫我們增加 inserted_atupdated_at 這兩個欄位的 timestamps()

(不是同一個,這邊的 Ecto.Migration.timestamps/1 是幫我們加欄位,剛剛的是 Ecto.Schema.timestamps/1 幫我們定義型態,不過現在不用太在意他們的細節)。

執行 migration

我們現在已經有了 Schema 跟 migration 檔案,接下來就是要執行 migration 了,執行 migration 的指令是:

mix ecto.migrate

執行後會看到:

Compiling 1 file (.ex)
Generated gratitude app

23:01:23.264 [info] == Running 20230922133805 Gratitude.Repo.Migrations.CreateNotes.change/0 forward

23:01:23.265 [info] create table notes

23:01:23.266 [info] == Migrated 20230922133805 in 0.0s

操作看看

現在我們成功地在資料庫建立了我們需要的表格與欄位,接下來我們來直接使用 Ecto 來操作看看。

執行 iex -S mix 進入這個專案的 iex :

撈出所有的 Note

Gratitude.Repo.all(Gratitude.Notes.Note)
#=> []

我們什麼都沒有拿到,因為我們還沒有任何資料,但我們這個時候也發現,Gratitude.RepoGratitude.Notes.Note 實在是很攏長,還好我們可以在要使用他們之前先 alias。

alias Gratitude.Repo
alias Gratitude.Notes.Note

Repo.all(Note)

新增資料

剛剛使用的 Gratitude.Repo 是 Ecto 提供給我們專案的 Module,裡面實作了所有 Ecto.Repo 這個 behaviour 規定用來操作資料庫的函式,一樣都可以在 Ecto.Repo 文件中找到
(behaviour 不在此次範圍,可以參考文件,但是現在先不用太在意,只需要知道 Gratitude.Repo 的方法可以在 Ecto.Repo 文件 找到即可)

我們先使用 insert/2 來新增幾筆資料:

  Repo.insert(%Note{content: "真的很感謝今天有記得帶傘"})
  #=>
  {:ok,
  %Gratitude.Notes.Note{
    __meta__: #Ecto.Schema.Metadata<:loaded, "notes">,
    id: 1,
    content: "真的很感謝今天有記得帶傘",
    inserted_at: ~N[2023-09-22 14:11:21],
    updated_at: ~N[2023-09-22 14:11:21]
  }}

可以看到回傳的結果是典型的成功 tuple,第一個元素是 :ok,第二個元素則是我們新增的資料,裡面有他在資料庫的 id 與其他欄位。

再新增幾個

Repo.insert(%Note{content: "剛剛有人幫我按著電梯門"})
Repo.insert(%Note{content: "今天便當的炸蝦好好吃"})

查詢資料

這個時候我們再查詢一次全部的 Note

Repo.all(Note)

就會看到我們剛剛輸入的項目了
下一篇我們會在 Context 裡面把這些操作包裝起來,讓我們可以容易的在其他地方使用。


上一篇
7 Phoenix 專案結構
下一篇
9 Context
系列文
Phoenix 1.7 完全教學30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言